﻿using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Documents;
using Notoric.Gui.Windows;

namespace Notoric.Gui.Commands
{
    /// <summary>
    /// A common base class for commands that manipulate selection in <see cref="ViewModel.Elements.RtfTextSectionInterior"/>
    /// and need to react to its changes.
    /// </summary>
    public abstract class RtfTextSelectionCommand : RibbonCommand
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="RtfTextSelectionCommand"/> class.
        /// </summary>
        /// <param name="mainWindow">
        /// <para>
        /// Type: <see cref="MainWindow"/>
        /// </para>
        /// <para>
        /// The main application window.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="mainWindow"/> is <see langword="null"/>.
        /// </exception>
        protected RtfTextSelectionCommand(MainWindow mainWindow)
            : base(mainWindow)
        {
            mainWindow.PropertyChanged += new PropertyChangedEventHandler(mainWindow_PropertyChanged);
        }

        #endregion

        #region ICommand Members

        /// <inheritdoc/>
        public override bool CanExecute(object parameter)
        {
            return activePage != null && activePage.SelectedContent == ViewModel.Elements.SelectedContentType.Text && CanExecuteCore(parameter);
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Occurs when a property of the main application window has changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void mainWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (activePage != null)
            {
                activePage.SelectedSections.CollectionChanged -= SelectedSections_CollectionChanged;

                foreach (var section in activePage.SelectedSections)
                    section.Interior.PropertyChanged -= section_PropertyChanged;
            }

            activePage = ApplicationWindow.ActivePage;

            if (activePage != null)
            {
                activePage.SelectedSections.CollectionChanged += new NotifyCollectionChangedEventHandler(SelectedSections_CollectionChanged);

                foreach (var section in activePage.SelectedSections)
                {
                    if (section.Interior is ViewModel.Elements.RtfTextSectionInterior)
                        section.Interior.PropertyChanged += new PropertyChangedEventHandler(section_PropertyChanged);
                }
            }
        }

        /// <summary>
        /// Occurs when a property of a selected RTF text section has changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void section_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Selection")
                SelectionChanged((ViewModel.Elements.RtfTextSectionInterior)sender);
        }

        /// <summary>
        /// Occurs when the collection of selected sections of the active page has changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void SelectedSections_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (ViewModel.Elements.Section section in e.NewItems)
                    {
                        if (section.Interior is ViewModel.Elements.RtfTextSectionInterior)
                            section.Interior.PropertyChanged += new PropertyChangedEventHandler(section_PropertyChanged);
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (ViewModel.Elements.Section section in e.OldItems)
                        section.Interior.PropertyChanged -= section_PropertyChanged;
                    if (activePage.SelectedContent != ViewModel.Elements.SelectedContentType.Text)
                        SelectionChanged(null);
                    break;
                case NotifyCollectionChangedAction.Reset:
                    foreach (var section in activePage.Sections)
                        section.Interior.PropertyChanged -= section_PropertyChanged;
                    SelectionChanged(null);
                    break;
            }
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// When overriden in a derived class, this method returns a value indicating
        /// whether the command can be executed or not.
        /// </summary>
        /// <param name="parameter">
        /// <para>
        /// Type: <see cref="Object"/>
        /// </para>
        /// <para>
        /// Execution parameter.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="Boolean"/>
        /// </para>
        /// <para>
        /// <see langword="true"/> if the command can execute; <see langword="false"/> otherwise.
        /// </para>
        /// </returns>
        protected abstract bool CanExecuteCore(object parameter);

        /// <summary>
        /// This method is called when the active text selection has changed.
        /// </summary>
        /// <param name="interior">
        /// <para>
        /// Type: <see cref="ViewModel.Elements.RtfTextSectionInterior"/>
        /// </para>
        /// <para>
        /// The interior whose selection has changed.
        /// </para>
        /// </param>
        protected virtual void SelectionChanged(ViewModel.Elements.RtfTextSectionInterior interior)
        {
            RaiseCanExecuteChanged();
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the active page.
        /// </summary>
        private ViewModel.Elements.Page activePage;

        #endregion
    }
}
